﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SimpleData.ObjectModel.Collections
{
	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="TValue"></typeparam>
	public abstract class CollectionBase<TValue> : ICollection<TValue>
	{
		#region [ .ctor ]

		/// <summary>
		/// 
		/// </summary>
		protected CollectionBase()
			: this( CollectionCapabilities.All )
		{ }

		/// <summary>
		/// 
		/// </summary>
		protected CollectionBase( CollectionCapabilities capabilities )
		{
			this.InnerCollection = new List<TValue>();
			this.Capabilities = capabilities;
		}

		#endregion

		#region [ Events ]

		public event EventHandler<EventArgs> Adding;
		public event EventHandler<EventArgs> Added;
		public event EventHandler<EventArgs> Removing;
		public event EventHandler<EventArgs> Removed;
		
		#endregion 

		/// <summary>
		/// Indica se pode adicionar um item na coleção
		/// </summary>
		public bool CanAdd
		{
			get { return Convert.ToBoolean( this.Capabilities & CollectionCapabilities.CanAdd ); }
		}

		/// <summary>
		/// Indica se um item pode ser removido da coleção
		/// </summary>
		public bool CanRemove
		{
			get { return Convert.ToBoolean( this.Capabilities & CollectionCapabilities.CanRemove ); }
		}

		/// <summary>
		/// Obtém ou define o que a coleção pode fazer
		/// </summary>
		protected CollectionCapabilities Capabilities
		{
			get;
			set;
		}

		/// <summary>
		/// Obtém ou define a coleção interna do objeto
		/// </summary>
		protected IList<TValue> InnerCollection
		{
			get;
			private set;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="index"></param>
		/// <returns></returns>
		public TValue this[ int index ]
		{
			get { return this.InnerCollection[ index ]; }
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="action"></param>
		private void ThrowsIfNot( CollectionCapabilities action )
		{
			if ( this.IsReadOnly && action != CollectionCapabilities.ReadOnly )
				throw new InvalidOperationException( "A coleção é somente leitura." );
			else if ( !Convert.ToBoolean( this.Capabilities & action ) )
				throw new InvalidOperationException( "A coleção não pode ser alterada" );
		}

		public int IndexOf( TValue item )
		{
			return this.InnerCollection.IndexOf( item );
		}

		protected virtual void OnAdding( EventArgs args )
		{
			this.ThrowsIfNot( CollectionCapabilities.CanAdd );
			if ( this.Adding != null )
				this.Adding( this, args );
		}

		protected virtual void OnAdded( EventArgs args )
		{
			if ( this.Added != null )
				this.Added( this, args );
		}

		protected virtual void OnRemoving( EventArgs args )
		{
			this.ThrowsIfNot( CollectionCapabilities.CanRemove );
			if ( this.Removing != null )
				this.Removing( this, args );
		}

		protected virtual void OnRemoved( EventArgs args )
		{
			if ( this.Removed != null )
				this.Removed( this, args );
		}

		#region ICollection<TValue> Members

		public void Add( TValue item )
		{
			this.OnAdding( EventArgs.Empty );

			this.InnerCollection.Add( item );

			this.OnAdded( EventArgs.Empty );
		}

		public virtual void Clear()
		{
			this.ThrowsIfNot( CollectionCapabilities.CanRemove );
			this.InnerCollection.Clear();
		}

		public bool Contains( TValue item )
		{
			return this.InnerCollection.Contains( item );
		}

		public void CopyTo( TValue[] array, int arrayIndex )
		{
			this.InnerCollection.CopyTo( array, arrayIndex );
		}

		public int Count
		{
			get { return this.InnerCollection.Count; }
		}

		public bool IsReadOnly
		{
			get { return this.Capabilities == CollectionCapabilities.ReadOnly; }
		}

		public void RemoveAt( int index )
		{
			this.Remove( this[ index ] );
		}

		public virtual bool Remove( TValue item )
		{
			this.OnRemoving( EventArgs.Empty );
			bool wasRemoved = this.InnerCollection.Remove( item );
			if ( wasRemoved )
				this.OnRemoved( EventArgs.Empty );
			return wasRemoved;
		}

		#endregion

		#region IEnumerable<TValue> Members

		public IEnumerator<TValue> GetEnumerator()
		{
			return this.InnerCollection.GetEnumerator();
		}

		#endregion

		#region IEnumerable Members

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}

		#endregion
	}

	[Flags]
	public enum CollectionCapabilities
	{
		ReadOnly = 0,
		CanAdd = 1,
		CanRemove = 2,
		All = CanAdd | CanRemove
	}


}
